"""Support functions for loading level scripts."""

__author__ = 'Josh Oosterman'

from potv import geom
from potv.editor import editor
from potv.game import level
from potv.game import terrain
from potv.game import building
from potv.game import car
from potv.game import player

# Dummy class used to hold results.
class Namespace: pass

class LevelBuilder(object):
  """Used to build levels that can be played (but not edited)."""

  def __init__(self):
    self.objects = []

  def Meta(self, width, height, name=''):
    """A helper to define level metadata (mostly for debugging)."""
    self.name = name
    self.width = width
    self.height = height

  def CreateObject(self, type, **kwargs):
    """A helper to create an object (using type shortcuts)."""
    if 'location' in kwargs:
      loc = kwargs['location']
      kwargs['location'] = geom.Vector(loc[0], loc[1])
    obj = type(**kwargs)
    self.objects.append(obj)
    return obj

  def TerrainLayer(self, terrain_matrix):
    """A helper to create a layer of terrain (grass, sand etc) from an array."""
    terrain_tile_images = terrain.TerrainBuilder(terrain_matrix).Build()
    tile_renderer = terrain.TileRenderer(
        terrain_tile_images, terrain.TERRAIN_SIZE)
    self.objects.append(tile_renderer)

  def RoadLayer(self, road_type, road_matrix):
    """A helper to create a layer of roads."""
    road_tile_images = terrain.RoadBuilder(road_type, road_matrix).Build()
    tile_renderer = terrain.TileRenderer(
        road_tile_images, terrain.ROAD_SIZE)
    self.objects.append(tile_renderer)

  def GetLevel(self):
    """Creates the level instance and returns it."""
    result_level = level.Level(geom.Vector(self.width, self.height))
    for obj in self.objects:
      result_level.AddObject(obj)
    return result_level


class EditibleLevelBuilder(LevelBuilder):
  """Used to build editable levels (slower)."""

  def __init__(self):
    super(EditibleLevelBuilder, self).__init__()
    self.terrain_layer = []
    self.road_layers = []

  def CreateObject(self, type, **kwargs):
    result = super(EditibleLevelBuilder, self).CreateObject(type, **kwargs)
    result.Step = lambda: None
    return result

  def TerrainLayer(self, terrain_matrix):
    self.terrain_layer = terrain_matrix

  def RoadLayer(self, road_type, road_matrix):
    self.road_layers.append((road_type, road_matrix))

  def GetLevel(self):
    result = super(EditibleLevelBuilder, self).GetLevel()
    editor_control = editor.EditorControl(self.filename)
    result.AddObject(editor_control)
    editor_control.Setup(self.terrain_layer, self.road_layers)
    return result


def Load(filename, edit_mode=False):
  """Loads a level instance from the passed level script."""
  with open(filename) as f:
    body = f.read()

  if edit_mode:
    builder = EditibleLevelBuilder()
  else:
    builder = LevelBuilder()
  builder.filename = filename

  Meta = builder.Meta
  TerrainLayer = builder.TerrainLayer
  RoadLayer = builder.RoadLayer
  Create = builder.CreateObject

  # Helpers for creating common game objects.
  def Building(**kwargs):
    return Create(building.Building, **kwargs)
  def Car(**kwargs):
    return Create(car.Car, **kwargs)
  def Player(**kwargs):
    return Create(player.Player, **kwargs)

  exec(body) in locals(), globals()
  return builder.GetLevel()


def EmptyLevel(size, filename):
  """Creates an empty level for editing, including tiles."""
  result = level.Level(size)

  terrain_layer_size = size / terrain.TERRAIN_SIZE
  terrain_layer = []
  for _ in xrange(int(terrain_layer_size.y + 1)):
    terrain_layer.append([0] * int(terrain_layer_size.x + 1))

  road_layer_size = size / terrain.ROAD_SIZE
  road_layer = []
  for _ in xrange(int(road_layer_size.y + 1)):
    road_layer.append([0] * int(road_layer_size.x + 1))

  editor_control = editor.EditorControl(filename)
  result.AddObject(editor_control)
  editor_control.Setup(terrain_layer, [('asphalt', road_layer)])

  return result
